package com.heima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.article.mapper.ApArticleConfigMapper;
import com.heima.article.mapper.ApArticleContentMapper;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.mapper.ApCollectionMapper;
import com.heima.article.mapper.AuthorMapper;
import com.heima.article.service.ApArticleService;
import com.heima.feigns.behavior.BehaviorFeign;
import com.heima.feigns.user.UserFeign;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.article.dtos.ArticleHomeDto;
import com.heima.model.article.dtos.ArticleInfoDto;
import com.heima.model.article.dtos.ArticleVisitStreamMess;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.article.pojos.ApArticleConfig;
import com.heima.model.article.pojos.ApArticleContent;
import com.heima.model.article.pojos.ApAuthor;
import com.heima.model.article.pojos.ApCollection;
import com.heima.model.article.vos.HotArticleVo;
import com.heima.model.behavior.dtos.ApArticleRelationDto;
import com.heima.model.common.constants.ArticleConstants;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.user.pojos.ApUser;
import com.heima.model.user.pojos.ApUserFollow;
import com.heima.utils.threadlocal.AppThreadLocalUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


@Service
@Transactional
@Slf4j
public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle> implements ApArticleService {


    private static final int MAX_PAGE_SIZE = 50;

    @Autowired
    AuthorMapper authorMapper;

    @Autowired
    ApArticleConfigMapper apArticleConfigMapper;
    @Autowired
    ApArticleContentMapper apArticleContentMapper;

    @Autowired
    ApArticleMapper apArticleMapper;

    @Value("${file.oss.web-site}")
    private String webSite;


    @Autowired
    UserFeign userFeign;
    @Autowired
    BehaviorFeign behaviorFeign;

    @Autowired
    ApCollectionMapper apCollectionMapper;

    @Autowired
    KafkaTemplate kafkaTemplate;

    @Autowired
    RedisTemplate redisTemplate;


    /**
     * 更新数据库点赞等
     * 更新Redis缓存
     * @param mess
     */
    @Override
    public void updateApArticle(ArticleVisitStreamMess mess) {

        //1 查询文档
        ApArticle apArticle = getById(mess.getArticleId());
        if (apArticle == null) {
            return;
        }

        //2 修改文章的行为数据（阅读1、点赞3、评论5、收藏8）
        if (mess.getView() != 0) {
            apArticle.setViews((int) (apArticle.getViews() == null ? mess.getView() : mess.getView() + apArticle.getViews()));
        }
        if (mess.getLike() != 0) {
            int like = (int) (apArticle.getLikes() == null ? mess.getLike() : mess.getLike() + apArticle.getLikes());
            apArticle.setLikes(like);
        }
        if (mess.getComment() != 0) {
            int comment = (int) (apArticle.getComment() == null ? mess.getComment() : mess.getComment() + apArticle.getComment());
            apArticle.setComment(comment);
        }
        if (mess.getCollect() != 0) {
            int collection = (int) (apArticle.getCollection() == null ? mess.getCollect() : mess.getCollect() + apArticle.getCollection());
            apArticle.setCollection(collection);
        }
        updateById(apArticle);


        //3 计算文章分值
        Integer score = computeScore(apArticle);
        score = score*3;  //当天热点数据 *3


        //4 更新缓存（频道）
        // 当前文章的频道下的集合
        String hotArticleListStr = (String) redisTemplate.opsForValue().get(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + apArticle.getChannelId());
        // 更新redis缓存
        updateArticleCache(apArticle, score, hotArticleListStr, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + apArticle.getChannelId());

        //5 更新推荐列表的缓存
        String hotAllArticleListStr = (String) redisTemplate.opsForValue().get(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + ArticleConstants.DEFAULT_TAG);
        // 更新redis缓存
        updateArticleCache(apArticle, score, hotAllArticleListStr, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + ArticleConstants.DEFAULT_TAG);

    }

    /**
     * 更新Redis中文章的分数，重新排序
     * @param apArticle 当前文章
     * @param score  实时计算后的分值
     * @param hotArticleListStr Redis缓存热点文章字符串
     * @param key redis key
     */
    private void updateArticleCache(ApArticle apArticle, Integer score,
                                    String hotArticleListStr, String key) {
        if (StringUtils.isNotBlank(hotArticleListStr)) {

            boolean flag = false;

            // 缓存中的热点文章集合
            List<HotArticleVo> articleVos = JSON.parseArray(hotArticleListStr, HotArticleVo.class);

            for (HotArticleVo articleVo : articleVos) {
                if (articleVo.getId().equals(apArticle.getId())) {
                    articleVo.setScore(articleVo.getScore() + score);
                    flag = true;
                    break;
                }

            }

            // 缓存中没有当前文章
            if (!flag) {
                // 排序
                articleVos = articleVos.stream().sorted(Comparator.comparing(HotArticleVo::getScore).reversed()).collect(Collectors.toList());

                HotArticleVo lastHotArticleVo = articleVos.get(articleVos.size() - 1);
                if (lastHotArticleVo.getScore() < score) {
                    // 判断文章集合是否小于 30
                    if (articleVos.size() < 30) {  // 直接添加到缓存

                        HotArticleVo hotArticleVo = new HotArticleVo();
                        BeanUtils.copyProperties(apArticle, hotArticleVo);
                        hotArticleVo.setScore(hotArticleVo.getScore() + score);

                        articleVos.add(hotArticleVo);
                    } else {
                        // 移除最后一条
                        articleVos.remove(lastHotArticleVo);

                        // 追加当前文章到热点文章
                        HotArticleVo hotArticleVo = new HotArticleVo();
                        BeanUtils.copyProperties(apArticle, hotArticleVo);
                        hotArticleVo.setScore(hotArticleVo.getScore() + score);

                        articleVos.add(hotArticleVo);
                    }
                }

            }

            // 对 数据排序
            articleVos = articleVos.stream().sorted(Comparator.comparing(HotArticleVo::getScore).reversed()).collect(Collectors.toList());

            // 更新到缓存
            redisTemplate.opsForValue().set(key, JSON.toJSONString(articleVos));
        }
    }


    /**
     * 2.1计算文章分值算法
     * @param apArticle
     * @return
     */
    private Integer computeScore(ApArticle apArticle) {
        int score = 0;
        // 阅读 1
        if (apArticle.getViews() != null) {
            score += apArticle.getViews() * ArticleConstants.HOT_ARTICLE_VIEW_WEIGHT;
        }
        // 点赞 3
        if (apArticle.getLikes() != null) {
            score += apArticle.getLikes() * ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT;
        }
        // 评论 5
        if (apArticle.getComment() != null) {
            score += apArticle.getComment() * ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT;
        }
        // 收藏 8
        if (apArticle.getCollection() != null) {
            score += apArticle.getCollection() * ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT;
        }

        return score;
    }

    /**
     * 加载文章详情的初始化配置信息，比如关注、喜欢、不喜欢等
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult loadArticleBehavior(ArticleInfoDto dto) {

        //1 参数校验
        if(dto == null || dto.getArticleId() == null || dto.getAuthorId() == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //2 查询关注  用户微服务
        boolean isfollow=false, iscollection=false;

        ApUser user = AppThreadLocalUtils.getUser();
        ApAuthor apAuthor = authorMapper.selectById(dto.getAuthorId());
        if (user != null && apAuthor!=null) {
            ApUserFollow userFollow = userFeign.findByUserIdAndFollowId(user.getId(), apAuthor.getUserId());
            if (userFollow != null) {
                isfollow=true;
            }

        }

        //3 查询点赞不喜欢  行为微服务
        ApArticleRelationDto relationDto =
                new ApArticleRelationDto(dto.getArticleId(),user.getId(), (short) 1);
        Map relationMap = behaviorFeign.findApArticleRelation(relationDto);

        //4 查询收藏  文章微服务
        ApCollection apCollection =apCollectionMapper.selectOne(Wrappers.<ApCollection>lambdaQuery()
                .eq(ApCollection::getArticleId,dto.getArticleId())
                .eq(ApCollection::getEntryId, relationMap.get("entryId") )
                .eq(ApCollection::getType,0)
            );
        if (apCollection != null) {
            iscollection = true;
        }


        //5 返回结果
        Map resultMap = new HashMap();
        resultMap.put("isfollow", isfollow);
        resultMap.put("iscollection", iscollection);
        resultMap.putAll(relationMap);
        return ResponseResult.okResult(resultMap);
    }

    /**
     * 查询文章详情
     * @param dto 文章id
     * @return
     */
    @Override
    public ResponseResult loadArticleInfo(ArticleInfoDto dto) {
        // 1 参数检查
        if (dto==null || dto.getArticleId() == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        Long id = dto.getArticleId();

        //2 查询文章配置信息config
        ApArticleConfig articleConfig = apArticleConfigMapper.selectOne(Wrappers.<ApArticleConfig>lambdaQuery().eq(ApArticleConfig::getArticleId, id));
        if (articleConfig == null
                || articleConfig.getIsDown()
                || articleConfig.getIsDelete()) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "文章已下架");
        }

        //3 查询文章内容
        ApArticleContent articleContent = apArticleContentMapper.selectOne(Wrappers.<ApArticleContent>lambdaQuery().eq(ApArticleContent::getArticleId, id));
        if (articleContent == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "文章不存在");
        }

        //4 返回封装结果
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("config", articleConfig);
        resultMap.put("content", articleContent);

        return ResponseResult.okResult(resultMap);
    }

    /**
     * 根据参数加载文章列表  v2
     *
     * @param loadtypeLoadMore
     * @param dto
     * @param firstPage
     * @return
     */
    @Override
    public ResponseResult load2(Short loadtypeLoadMore, ArticleHomeDto dto, boolean firstPage) {
        if (firstPage) {// 首页

            String hotListStr = (String) redisTemplate.opsForValue().get(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + dto.getTag());

            if (StringUtils.isNotBlank(hotListStr)) {
                List<HotArticleVo> articleVos = JSON.parseArray(hotListStr, HotArticleVo.class);
                ResponseResult result = ResponseResult.okResult(articleVos);
                result.setHost(webSite);

                return result;
            }

        }


        return load(loadtypeLoadMore,dto);
    }

    /**
     * 根据参数加载文章列表
     * @param loadtype 1为加载更多  2为加载最新
     * @param dto
     * @return
     */
    @Override
    public ResponseResult load(Short loadtype, ArticleHomeDto dto) {
        //1 参数检查
        if (dto == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        // 频道
        if (StringUtils.isBlank(dto.getTag())) {
            dto.setTag(ArticleConstants.DEFAULT_TAG);
        }
        // 页大小
        Integer size = dto.getSize();
        if (size == null || size <= 0) {
            size = 10;
        }
        size = Math.min(size, MAX_PAGE_SIZE);
        dto.setSize(size);

        // 时间
        if (dto.getMinBehotTime() == null) {
            dto.setMinBehotTime(new Date());
        }
        if (dto.getMaxBehotTime() == null) {
            dto.setMaxBehotTime(new Date());
        }

        // loadtype
        if (loadtype==null ||
                !loadtype.equals(ArticleConstants.LOADTYPE_LOAD_NEW) ||
                !loadtype.equals(ArticleConstants.LOADTYPE_LOAD_MORE)){
            loadtype = ArticleConstants.LOADTYPE_LOAD_MORE;
        }

        //2 执行查询
        List<ApArticle> articleList = apArticleMapper.loadArticleList(dto, loadtype);

        //3 封装结果
        ResponseResult result = ResponseResult.okResult(articleList);
        result.setHost(webSite);

        return result;
    }

    /**
     * 保存app文章
     * @param articleDto
     * @return
     */
    @Override
    public ResponseResult saveArticle(ArticleDto articleDto) {
        // 参数检查
        if (articleDto == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        //1 保存article 文章数据， 设置作者id
        ApArticle apArticle = new ApArticle();
        BeanUtils.copyProperties(articleDto, apArticle);
        // 设置作者的id
        ApAuthor apAuthor = authorMapper.selectOne(Wrappers.<ApAuthor>lambdaQuery()
                .eq(ApAuthor::getName, apArticle.getAuthorName()));
        if (apAuthor == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        apArticle.setAuthorId(apAuthor.getId().longValue());

        if (articleDto.getId() == null) {  // 保存

            // 普通文章
            apArticle.setComment(0);
            apArticle.setViews(0);
            apArticle.setCollection(0);
            apArticle.setLikes(0);
            // 保存
            save(apArticle);  // 返回ID

            //2 保存article_config 数据
            ApArticleConfig apArticleConfig = new ApArticleConfig();
            apArticleConfig.setIsForward(true);
            apArticleConfig.setIsComment(true);
            apArticleConfig.setIsDown(false);
            apArticleConfig.setIsDelete(false);
            // 文章id
            apArticleConfig.setArticleId(apArticle.getId());
            apArticleConfigMapper.insert(apArticleConfig);

            //3 保存文章article_content 数据
            ApArticleContent apArticleContent = new ApArticleContent();
            apArticleContent.setContent(articleDto.getContent());
            // 文章ID
            apArticleContent.setArticleId(apArticle.getId());
            apArticleContentMapper.insert(apArticleContent);


        }else { // 修改

            // 查询文章
            ApArticle article = getById(articleDto.getId());
            if (article == null) {
                log.info("ApArticle getById is null: {}", articleDto.getId());

                return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
            }

            // 修改 apArticle
            updateById(apArticle);

            // 修改 apArticleContent
            ApArticleContent articleContent = apArticleContentMapper.selectOne( Wrappers.<ApArticleContent>lambdaQuery()
                    .eq(ApArticleContent::getArticleId,articleDto.getId() ));
            if (articleContent == null) {
                log.info("articleContent getById is null: {}", articleDto.getId());
                return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
            }
            articleContent.setContent(articleDto.getContent());
            apArticleContentMapper.updateById(articleContent);

        }

        //导入数据到es索引库  发送消息
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("id", apArticle.getId().toString());
        map.put("publishTime", apArticle.getPublishTime());
        map.put("layout", apArticle.getLayout());
        map.put("images", apArticle.getImages());
        map.put("authorId", apArticle.getAuthorId());
        map.put("title", apArticle.getTitle());
        // 发送消息 给 search服务同步索引库
        kafkaTemplate.send(ArticleConstants.ARTICL_ES_SYNC_TOPIC, JSON.toJSONString(map));


        //4 返回结果  文章保存后的主键 ID
        return ResponseResult.okResult(apArticle.getId());
    }

}
